/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * kernel.cc																*
 *																			*
 * The entry point to the C++ part of the kernel, handles a lot of runtime	*
 * stuff and gets the kernel ready to start interacting with the user.		*
 *																			*
\***************************************************************************/

// includes
#include <system.h>
#include <iostream>
#include <console.h>
#include <memory.h>
#include <task.h>
#include <disk.h>
#include <gui.h>
#include <keyboard.h>
#include <filesys.h>
#include <multiboot.h>
#include <stdio.h>
#include <paging.h>
#include <stddef.h>
#include <io.h>

// main() must be C so no name mangling occurs
extern "C" int main( multiboot_info_t* mb, unsigned int magic_number );

// global console
Console cons;

/****** GCC EXTERNAL ASSIST FUNCTIONS ******/

// they must be in C
extern "C"
        {
        int __cxa_atexit(void (*f)(void *), void *p, void *d);
        void __cxa_finalize(void *d);
        };

// the DSO handle
void *__dso_handle; /*only the address of this symbol is taken by gcc*/

// one of the global objects
struct object
{
        void (*f)(void*);
        void *p;
        void *d;
} object[32] = {0};

// number of objects
unsigned int iObject = 0;

// sets up the destructors
int __cxa_atexit(void (*f)(void *), void *p, void *d)
{
	if (iObject >= 32) return -1;
	object[iObject].f = f;
	object[iObject].p = p;
	object[iObject].d = d;
	++iObject;
	return 0;
}

// this destroys all objects
void __cxa_finalize(void *d)
{
	unsigned int i = iObject;
	for (; i > 0; --i)
	{
		--iObject;
		object[iObject].f(object[iObject].p);
	}
}

// new/delete operators

//overload the operator "new"
void* operator new (size_t size)
{
    return kmalloc(size);
}

//overload the operator "new[]"
void* operator new[] (size_t size)
{
    return kmalloc(size);
}

//overload the operator "delete"
void operator delete (void * p)
{
    kfree( p );
}

//overload the operator "delete[]"
void operator delete[] (void * p)
{
    kfree( p );
}

// pure virtual function failed
extern "C" void __cxa_pure_virtual()
{
	kprintf( "Pure virtual function call (=0) caught!\n" );
}

/****** Real Kernel ******/

// STDOUT character output
int stdout_putc( int c )
{
	// print it (this hack is to get it to print safely)
	char tmp[2] = { c, 0 };
	cons.Output( tmp );
}

// STDIN character input
int stdin_getc()
{
	// get from the keyboard buffer
	return getch();
}

// STDOUT 'write' function
int stdout_write( int fd, void* buff, uint_t count )
{
	// create a temporary char* pointer to the void* buffer
	char* tmp = (char*) buff;
	
	// print it all
	for( int i = 0; i < count; i++ )
		putc( fd, tmp[i] );
	
	// return the count
	return count;
}

// STDIN 'read' function
int stdin_read( int fd, void* buff, uint_t count )
{
	// create a temporary char* pointer to the void* buffer
	char* tmp = (char*) buff;
	
	// print it all
	cons.Get( tmp, count );
	
	// return the count
	return count;
}

// idle procedure
void nullproc()
{
	// stdout and stdin device descriptors
	int stdout, stdin;
	
	// install stdout and stdin (both refer to the same device)
	stdout = stdin = InstallDevice( "console", 0, 0 );
	
	// install the functions (both stdin and stdout are the same)
	InstallFunctions( stdout, (IOCtlIntFunc) NULL, (IOCtlPtrFunc) NULL, stdin_read, stdout_write, (LSeekFunc) NULL, stdin_getc, stdout_putc );
	
	// null procedure, called when nothing else to do
	cons.Output( "[NULL] Idle task has started.\n", true );
	
	// initialize the FAT32 system
	cons.Output( "[FAT ] Initializing FAT32 structures... ", true );
	InitBPB_FAT32( 0 );
	cons.Output( "Done!\n", true );
	
	// print the welcome
	kprintf( "Welcome to Mattise!\nVersion 1.1, ALPHA\n" );
	
	// start the GUI
	//CreateTask( (unsigned int) GUI, 1024 * 1024, "GUI", 10 );

	// idle
	while( true );
}

// global multiboot info pointer
multiboot_info_t* MultiBootHeader;

// kernel entry point
int main( multiboot_info_t* mb, unsigned int magic_number )
{
	// turn on debugging
	cons.ToggleDebugMode();
	
	// clear the screen
	cons.ClearScreen();
	
	// kill interrupts
	__asm__ __volatile__ ( "cli" );
	
	// install the GDT
	cons.Output( "[GDT ] Installing GDT... ", true );
	gdt_install();
	cons.Output( "Done!\n", true );
	
	// install the IDT
	cons.Output( "[IDT ] Installing IDT... ", true );
	idt_install();
	cons.Output( "Done!\n", true );
	
	// install ISRs
	cons.Output( "[INT ] Installing interrupts (ISR/IRQ)... ", true );
	isrs_install();
	
	// set up IRQ mapping
	irq_install();
	cons.Output( "Done!\n", true );

	// install the timer
	cons.Output( "[TMR ] Installing timer... ", true );
	install_timer();
	cons.Output( "Done!\n", true );
	
	// install the keyboard
	cons.Output( "[KBD ] Installing keyboard... ", true );
	keyboard_install();
	cons.Output( "Done!\n", true );
	
	// initialize the memory manager
	cons.Output( "[MEM ] Installing memory manager... ", true );
	kmalloc( 1 ); /** sets up the heap **/
	cons.Output( "Done!\n", true );
	
	// can interrupt now
	__asm__ __volatile__ ( "sti" );
	
	// detect hard drives
	cons.Output( "[IDE ] Detecting drives...\n", true );
	DetectDrives();
	cons.Output( "[IDE ] Done!\n", true );
	
	// setup the multitasker
	cons.Output( "[MTSK] Initializing multitasking... ", true );
	initmt();
	cons.Output( "Done!\n", true );
	
	// create system task
	cons.Output( "[MTSK] Creating idle task and enabling tasking... ", true );
	CreateTask( (unsigned int) nullproc, 1024 * 1024, "System Idle Process", 1 );
	cons.Output( "Done!\n", true );
	mtenable();

	// loop forever
	while( true );
}
